<?php
/*
 * @Author: 杨红兵 
 * @Date: 2022-07-13 14:35:36 
 * @Last Modified by: 杨红兵
 * @Last Modified time: 2022-08-08 13:32:26
 */

namespace app\libraries\utils;

use app\libraries\utils\traits\Signleton;

class Trace
{
    use Signleton;

    /**
     * 获取次数
     *
     * @var integer
     * @Author YangHB
     * @DateTime 2022-07-15 14:49:50
     *
     */
    private int $traceIndex = 0;

    /**
     * @param string
     */
    private string $traceId = '';

    /**
     * 父级跟踪 ID
     *
     * @var string
     * @Author YangHB
     * @DateTime 2022-07-12 13:23:12
     *
     */
    private string $preTraceId = '';

    /**
     * 当前请求 ID
     *
     * @var string
     * @Author YangHB
     * @DateTime 2022-07-12 13:23:45
     *
     */
    private string $nowTraceId = '';

    /**
     * 如果存在鉴权，该为鉴权类型
     *
     * @var string
     * @Author YangHB
     * @DateTime 2022-07-12 20:19:25
     *
     */
    private string $grantType = "";

    /**
     * 请求来源
     *
     * @var string
     * @Author YangHB
     * @DateTime 2022-07-12 20:19:38
     *
     */
    private string $comeFrom = "";

    /**
     * 返回全链路 ID
     *
     * @Author YangHB
     * @DateTime 2022-07-12 13:24:10
     *
     * @return string
     */
    public function getTraceId(): string
    {
        if (empty($this->traceId)) {
            $this->traceId = $this->getHeaderId("X_REQUEST_TRACE_ID");
        }
        return $this->traceId;
    }

    /**
     * 返回父级请求 ID
     *
     * @Author YangHB
     * @DateTime 2022-07-12 13:25:23
     *
     * @return string
     */
    public function getPreTraceId(): string
    {
        if (empty($this->preTraceId)) {
            $this->preTraceId = $this->getHeaderId("X_REQUEST_PRE_ID");
        }
        return $this->preTraceId;
    }

    /**
     * 重置关键信息
     *
     * @Author YangHB
     * @DateTime 2022-07-22 13:22:04
     *
     * @return void
     */
    public function resetTraceId()
    {
        $this->traceId = "";
        $this->preTraceId = "";
    }

    /**
     * 返回当前请求 ID
     *
     * @Author YangHB
     * @DateTime 2022-07-12 13:25:33
     *
     * @return string
     */
    public function getNowTraceId(): string
    {
        if (empty($this->nowTraceId)) {
            $this->nowTraceId = $this->getHeaderId("X_REQUEST_NOW_ID", true);
        }
        return $this->nowTraceId;
    }

    /**
     * 获取当前执行过程的标识 ID
     *
     * @return string
     */
    private function getHeaderId(string $header, bool $getDefault = false): string
    {
        $getItemId = '';
        $header = strtoupper($header);
        if (function_exists('app') && method_exists(app(), 'version')) {
            $request = app('request');
            $getItemId = $request->server($header, '');
            empty($getItemId) && $getItemId = $request->server('HTTP_' . $header, '');
        } else {
            $getItemId = $_SERVER[$header] ?? '';
            empty($getItemId) && $getItemId = ($_SERVER['HTTP_' . $header] ?? '');
        }
        return $getDefault === true && empty($getItemId) ? get_sequuid() : $getItemId;
    }

    /**
     * 返回鉴权类型
     *
     * @Author YangHB
     * @DateTime 2022-07-12 20:22:23
     *
     * @return string
     */
    public function getGrantType(): string
    {
        if (!empty($this->grantType)) {
            return $this->grantType;
        }
        /**
         * @var \think\Request
         */
        $request = app('request');
        if ($request) {
            $this->grantType = $request->all('appType') ?? '';
        }
        if (empty($this->grantType)) {
            $this->grantType = "none";
        }

        return $this->grantType;
    }

    /**
     * 返回请求来源
     *
     * @Author YangHB
     * @DateTime 2022-07-12 20:22:23
     *
     * @return string
     */
    public function getComeFrom(): string
    {
        if (!empty($this->comeFrom)) {
            return $this->comeFrom;
        }
        /**
         * @var \think\Request
         */
        $request = app('request');
        if ($request) {
            $this->comeFrom = $request->all('come_form') ?? '';
        }
        if (empty($this->comeFrom)) {
            $this->comeFrom = "none";
        }

        return $this->comeFrom;
    }

    /**
     * 判断是不是在命令行模式，需要注意排除 swoole
     *
     * @Author YangHB
     * @DateTime 2022-07-12 08:47:19
     *
     * @return boolean
     */
    public function isCommand(): bool
    {
        $request = app('request');
        if (
            php_sapi_name() === "cli" && $request
            && (strripos($request->baseUrl(false), config('app.url', 'http://localhost')) !== false
                || $request->baseUrl(false) === "swoole")
        ) {
            return true;
        }
        return false;
    }

    /**
     * 返回 CLI 命令行模式的执行信息
     *
     * @return string
     */
    public function getCommand(): string
    {
        /**
         * @var \think\Request
         */
        $request = app('request');
        if (
            $request && (strripos($request->baseUrl(false), config('app.url', 'http://localhost')) !== false
                || $request->baseUrl(false) === "swoole")
        ) {
            $requestArgs = $request->server('argv');
            if (empty($requestArgs)) {
                //头源数据中为小写，直接获取会自动大写转换
                $requestArgs = $request->server()['argv'] ?? [];
            }
            if (empty($requestArgs)) {
                $requestArgs = $_SERVER['argv'] ?? [];
            }
            return sprintf('%s %s', str_replace("/usr/bin/nohup", "php", $request->server('_') ?? ''), implode(' ', (array)$requestArgs));
        }
        return sprintf('%s %s', str_replace("/usr/bin/nohup", "php", $_SERVER['_'] ?? ''), implode(' ', (array)($_SERVER['argv'] ?? [])));
    }

    /**
     * 返回请求域名
     *
     * @Author YangHB
     * @DateTime 2022-07-12 13:29:50
     *
     * @return string
     */
    public function getHost(): string
    {
        return app('request')->host(true);
    }

    /**
     * 返回 FPM 模式的请求信息
     *
     * @return string
     */
    public function getUrl(): string
    {
        $request = app('request');
        if ($request) {
            return $request->url(true);
        }
        return sprintf('%s://%s%s', $_SERVER['REQUEST_SCHEME'] ?? 'http', $_SERVER['HTTP_HOST'] ?? '', $_SERVER['REQUEST_URI'] ?? '');
    }

    /**
     * 返回请求路由
     *
     * @Author YangHB
     * @DateTime 2022-09-21 10:36:59
     *
     * @return string
     */
    public function getPath(): string
    {
        /**
         * @var \think\Request
         */
        $request = app('request');
        if (
            php_sapi_name() === "cli" && $request
            && (strripos($request->baseUrl(false), config('app.url', 'http://localhost')) !== false
                || $request->baseUrl(false) === "swoole")
        ) {
            return '/';
        }
        if ($request) {
            return $request->baseUrl(false);
        }
        return $_SERVER['PATH_INFO'] ?? '';
    }

    /**
     * 返回当前请求的数据体
     *
     * @return string
     */
    public function getRequestContent(): string
    {
        $request = app('request');
        if ($request) {
            return $request->getContent();
        }
        return '';
    }

    /**
     * 返回执行文件位置
     *
     * @Author YangHB
     * @DateTime 2022-07-15 14:50:30
     *
     * @return array
     */
    public function getFileLine(): array
    {
        //获取调用文件位置
        $fileLine = [];
        $shiftNum = config('log.log_trace_stack_ignore', 0);
        $limit = (10 + $shiftNum) < 15 ? (10 + $shiftNum) : 15;
        $debugInfo = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, $limit);
        $appPath = app()->getRootPath();
        foreach ($debugInfo as $value) {
            if (empty($value['file']) || !isset($value['line'])) {
                continue;
            }
            if (
                stripos($value['file'], '/think/Container.php') !== false
                || stripos($value['file'], '/Sandbox.php') !== false
                || stripos($value['file'], '/WithApplication.php') !== false
                || stripos($value['file'], '/InteractsWithServer.php') !== false
                || stripos($value['file'], '/think/console/Command.php') !== false
                || stripos($value['file'], '/think/Console.php') !== false
                || stripos($value['file'], '/log/File.php') !== false
                || stripos($value['file'], '/MultiApp.php') !== false
                || stripos($value['file'], '/log/Channel.php') !== false
                || stripos($value['file'], '/think/Log.php') !== false
                || stripos($value['file'], '/think/Http.php') !== false
                || stripos($value['file'], '/think/dispatch/Controller.php') !== false
                || stripos($value['file'], '/think/Pipeline.php') !== false
                || stripos($value['file'], '/route/dispatch/Controller.php') !== false
                || stripos($value['file'], '/think/route/Dispatch.php') !== false
                || stripos($value['file'], '/think/Route.php') !== false
                || stripos($value['file'], '/concerns/InteractsWithHttp.php') !== false
                || stripos($value['file'], '/app/common.php') !== false
                || stripos($value['file'], '/phpunit') !== false
                || stripos($value['file'], '/think/Event.php') !== false
                || stripos($value['file'], '/think/Facade.php') !== false
                || stripos($value['file'], '/src/helper.php') !== false
                || stripos($value['file'], '/EventListen.php') !== false
                || stripos($value['file'], 'app/ExceptionHandle.php') !== false
                || stripos($value['file'], 'Middleware.php') !== false // 只要是中间件的，一律不写文件名
                || stripos($value['file'], '/BaseCommand.php') !== false
                || stripos($value['file'], '/Dispatcher.php') !== false
                || stripos($value['file'], '/InteractsWithRpcServer.php') !== false
                || stripos($value['file'], '/queue/Worker.php') !== false
                || stripos($value['file'], '/InteractsWithQueue.php') !== false
                || stripos($value['file'], '/queue/Job.php') !== false
                || stripos($value['file'], '/queue/SyncJob.php') !== false
                || stripos($value['file'], '/queue/AsyncJob.php') !== false
                || stripos($value['file'], '/queue/CmdJob.php') !== false
                || stripos($value['file'], '/queue/NohupJob.php') !== false
                || stripos($value['file'], '/cron/Task.php') !== false
                || stripos($value['file'], '/cron/Scheduler.php') !== false
                || stripos($value['file'], '/command/Run.php') !== false
                || stripos($value['file'], '/event/ManagerStart.php') !== false
                || stripos($value['file'], '/Scheduler.php') !== false
            ) {
                continue;
            }
            if (stripos($value['file'], '/think') !== false && ($value['line'] ?? 0) < 15) {
                continue;
            }
            $fileLine[] = str_replace($appPath, "", $value['file']) . ':' . $value['line'] ?? 0;
        }
        return $fileLine;
    }

    /**
     * 返回请求 ID
     *
     * @Author YangHB
     * @DateTime 2022-07-15 14:50:41
     *
     * @return int
     */
    public function getTraceIndex(): int
    {
        return $this->traceIndex++;
    }
}
